﻿using System;
using System.Collections; 
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace Parseq.Util
{
    public class InputStream : Stream , IEnumerable
    {
        private long position;
        private BufferList list; 
        private Stream source;
        private bool isEOF = false; 
        public InputStream(Stream s)
        {
            this.Stream = s; 
        }

        public Stream Stream
        {
            set
            {
                if (value != this.source) // if it's the same we do nothing. 
                {
                    this.source = value; // can be NULL
                    this.list = null; // whenver we set the Stream we'll start over the bufferlist. 
                    if (this.source != null)
                    {
                        this.list = new BufferList();
                        this.list.ReadStream(this.source);
                    }
                    this.position = 0;
                }
            }
        }

        public override bool CanRead
        {
            get { return true; }
        }

        public override bool CanSeek
        {
            get { return true; }
        }

        public override bool CanWrite
        {
            get { return false; }
        }

        public override long Length /* length is unkonwn from the very beginning */
        {
            get { throw new NotImplementedException(); }
        }
        
        public override long Position
        {
            get
            {
                return this.position; 
            }
            set
            {
                if (value < 0)
                {
                    throw new IndexOutOfRangeException("Position cannot be less than zero.");
                }
                this.position = value; // we'll need to recalculate the bufferPos.
            }
        }

        public override void Flush()
        {
            // do nothing. 
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            this.Position = this.position + offset;
            return this.position; 
        }

        public override void SetLength(long value)
        {
            throw new NotSupportedException("SetLength is not supported");
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            // the offset starts from where the given position is. 
            // the position will be calculated against where we are keeping the buffer... 
            long start = this.position + offset;
            long finish = start + count;
            while (finish >= this.list.Length) // we'll need to read. 
            {
                this.isEOF = !this.list.ReadStream(this.source);
                if (this.isEOF)
                    break;
            }
            finish = finish < this.list.Length ? finish : this.list.Length;
            for (long i = start; i < finish ; ++i) {
                buffer[i - start] = this.list[i];
            }
            this.Position = finish; 
            return (int)(finish - start); 
        }

        public bool IsEOF
        {
            get { return this.isEOF; }
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            throw new NotImplementedException();
        }

        public IEnumerator GetEnumerator()
        {
            return new InputStreamEnumerator(this);
        }
    }

    public class InputStreamEnumerator : IEnumerator
    {
        private long position; 
        private InputStream stream;
        private byte[] toRead = new byte[1]; 
        public InputStreamEnumerator(InputStream s)
        {
            this.stream = s; 
            this.position = s.Position; 
        }

        public object Current
        {
            get
            {
                return (byte)this.toRead[0]; 
            }
        }

        public bool MoveNext()
        {
            int bytesRead = this.stream.Read(this.toRead, 0, 1);
            if (bytesRead == 0)
            {
                return false;
            }
            else
            {
                return true; 
            }
        }

        public void Reset()
        {
            this.stream.Position = this.position; 
        }
    }

}
